#include "MainWindow.h"
#include <QApplication>

#define COUNTDOWN_TIME 60 * 3

#include "OutputWindow.h"
#include "PresetEditWindow.h"

#ifdef HAVE_MIDITCP
#include "SliderMidiInputAdapter.h"
#include "MidiInputSettingsDialog.h"
#endif

#include "http/LightingServer.h"


bool PresetValue::comparitor(PresetValue *a, PresetValue *b)
{
	return a->channel()->num() < b->channel()->num();
}

int PresetValue::effectiveValue(int masterValue)
{
	float floatVal = ((float)masterValue)/255.0;
	return (int)(m_value * floatVal);
}

int LightingPreset::effectiveValue(DmxChannel *chan)
{
	if(!chan)
		return -1;
	
	float floatVal = ((float)value())/255.0;
	foreach(PresetValue* val, m_values)
		if(!val->ignore() &&
		    val->channel() == chan)
			return (int)(val->value() * floatVal);
	return -1;
}

void LightingPreset::setValue(int value)
{
	float floatVal = ((float)value)/255.0;
	
	OutputWindow *out = MainWindow::inst()->outputWindow();
	out->holdTransmit(true);
	
	QList<LightingPreset *> presets = MainWindow::inst()->presets();
	
	foreach(PresetValue* val, m_values)
		if(!val->ignore())
		{
			DmxChannel *chan = val->channel();
			int max = 0;
			foreach(LightingPreset *preset, presets)
			{
				int ev = preset->effectiveValue(chan);
				if(ev > max)
					max = ev;
			}
			int fv = (int)(val->value() * floatVal);
			if(fv > max)
				max = fv;
			
			val->channel()->setValue(max);
			//qDebug() <<"LightingPreset::setValue: chan:"<<chan->name()<<", val:"<<fv<<", max:"<<max;
		}
		
	out->holdTransmit(false);
	
	SimpleChannel::setValue(value);
}

bool LightingPreset::fromVariantMap(const QVariantMap& map, bool flag)
{
	SimpleChannel::fromVariantMap(map, flag);
	
	m_values.clear();
	
	DmxChannelList chans = MainWindow::inst()->outputWindow()->channelList();
	
	QVariantList list = map["values"].toList();
	int presetIdx = 0;
	foreach(QVariant var, list)
	{
		QVariantMap data = var.toMap();
		int idx = data["chanIdx"].toInt();
		
		DmxChannel *chan = chans[idx];
		PresetValue *value = new PresetValue(chan);
		value->fromVariantMap(data);
		value->setIdx(presetIdx++);
		
		//qDebug() << "LightingPreset::fromVariantMap: "<<value->idx()<<" chan:"<<idx;
		
		m_values << value;
	}
	
	return true;
}

QVariantMap LightingPreset::toVariantMap()
{
	DmxChannelList chans = MainWindow::inst()->outputWindow()->channelList();
	QVariantMap map = SimpleChannel::toVariantMap();
	QVariantList list;
	foreach(PresetValue *val, m_values)
	{
		QVariantMap data = val->toVariantMap();
		int idx = chans.indexOf(val->channel());
		data["chanIdx"] = idx;
		
		//qDebug() << "LightingPreset::toVariantMap: "<<val->idx()<<" chan:"<<idx;
		list << data;
	}
	map["values"] = list;
	return map;
}

void MainWindow::storePresets()
{
	QVariantList list;
	foreach(LightingPreset *preset, m_presets)
	{
		list << preset->toVariantMap();
	}
	GetSettingsObject();
	settings.setValue("presets", list);
	//qDebug() << "MainWindow::storePresets: Stored "<<list.size()<<" presets";
}

void MainWindow::loadPresets()
{
	GetSettingsObject();
	QVariantList list = settings.value("presets").toList();
	foreach(QVariant var, list)
	{
		QVariantMap data = var.toMap();
		LightingPreset *preset = new LightingPreset();
		preset->fromVariantMap(data);
		addPreset(preset); 
	}
}


MainWindow *MainWindow::mw = 0;

MainWindow::MainWindow()
	: QWidget()
	, m_dmxClient(0)
	, m_host("localhost")
	, m_port(6159)
	, m_mainLevel(255)
	, m_isBlack(false)
	, m_switchEnabled(false)
	, m_countdownValue(COUNTDOWN_TIME)
	, m_outputWin(0)
	, m_blackAnim(0)
	, m_fadeSpeedIsTemp(false)
	, m_oldFadeSpeed(-1)
{
	// Store a pointer to our instance for the static MainWindow::inst()
	MainWindow::mw = this;
	
	setWindowTitle("DMX Lighting Control");
	setWindowIcon(QIcon("/usr/share/icons/Bluecurve/32x32/apps/icon-mixer.png"));
	
	if(!initClient())
	{
		QMessageBox::critical(this, "Unable to Connect to DMX Server","Unable to connect to DMX server - make sure server/dmxserver is running. The program probably won't work unless its running");
	}
	
	m_outputWin = new OutputWindow();
	
	setupGui();
	setWallSwitchEnabled(false);
	
	//showOutputWindow();
	
	loadSettings();
	
	#ifdef HAVE_MIDITCP
	// Cause it to create all the actions and load settings
	(void*)SliderMidiInputAdapter::instance();
	#endif
	
	m_httpServer = new LightingServer(8082);
}

MainWindow::~MainWindow()
{
	closeClient();
}

bool MainWindow::initClient()
{
	if(m_dmxClient)
	{
		closeClient();
		// closeClient() calls m_dmxClient->deleteLater(), so dont delete here - it also zeros the pointer for us
		//delete m_dmxClient
		//m_dmxClient = 0;
	}
		
	m_dmxClient = new DmxClient();
	
	// In current implementation, start() never returns false :-)
	if(!m_dmxClient->start(m_host, m_port))
		return false;
	
	// All the switch control logic (e.g. what do do when switch is turned on/off) is actually implemented
	// in the server (well, server/DmxSetter) - we just update the UI to tell the user the current status
	// of the switch - all just an FYI for the user. 
	connect(m_dmxClient, SIGNAL(switchToggled(bool)), this, SLOT(switchToggled(bool)));
	
	return true;
}

void MainWindow::closeClient()
{
	if(m_dmxClient)
	{
		m_dmxClient->exit();
		m_dmxClient->deleteLater();
		m_dmxClient = 0;
	}
}

void MainWindow::loadSettings()
{
	GetSettingsObject();
	m_host = settings.value("DmxClient/host",m_host).toString();
	m_port = settings.value("DmxClient/port",m_port).toInt();
	
	loadPresets();
}

void MainWindow::saveSettings()
{
	GetSettingsObject();
	settings.setValue("DmxClient/host",m_host);
	settings.setValue("DmxClient/port",m_port);
	
	settings.setValue("crossFadeSpeed",m_fadeSpeedBox->value());
	
	m_outputWin->storeChannels();
	storePresets();
}

void MainWindow::switchToggled(bool isSwitchOn)
{
	m_switchLabel->setText(QString("Switch: <b>%1</b>").arg(isSwitchOn ? "On" : "Off"));
}

void MainWindow::setWallSwitchEnabled(bool flag)
{
	m_switchEnabled = flag;
	
	m_dmxClient->setSwitchMaster(!flag);
	
	if(flag)
	{
		//m_switchPollTimer.start();
		m_expiresCountdownTimer.stop();
	}
	else
	{
		//m_switchPollTimer.stop();
		m_expiresCountdownTimer.start();
		m_countdownValue = COUNTDOWN_TIME;
		m_countdownLabel->setText(QString("Expires in %1 Minutes").arg(m_countdownValue));
		
		foreach(LightingPreset *preset, m_presets)
			preset->setValue(preset->value());
	}
	
	// All the preset controls
	m_controlWidgetBase->setEnabled(!flag);
	
	// Switch control UI elements
	m_switchLabel->setEnabled(flag);
	m_countdownLabel->setEnabled(!flag);
	m_resetCounterBtn->setEnabled(!flag);
}

void MainWindow::countdownExpires()
{
	m_countdownValue --;
	if(m_countdownValue <= 0)
	{
		// This button will trigger the setWallSwitchEnabled() slot above
		m_wallControlButton->setChecked(true);
	}
	m_countdownLabel->setText(QString("Expires in %1 Minutes").arg(m_countdownValue));
}

void MainWindow::resetCountdown()
{
	m_countdownValue = COUNTDOWN_TIME;
	m_countdownLabel->setText(QString("Expires in %1 Minutes").arg(m_countdownValue));
}

void MainWindow::setupGui()
{
	QVBoxLayout *vbox = new QVBoxLayout(this);
	
	QGroupBox *topBox = new QGroupBox("Wall Switch Control");
	topBox->setObjectName("topBox");
	vbox->addWidget(topBox);
	
	QGridLayout *topGrid = new QGridLayout(topBox);
	
	// Top-left part of the top grid: The "interactive control" button
	QRadioButton *btn1 = new QRadioButton("Interactive Control");
	btn1->setChecked(true);
	topGrid->addWidget(btn1,1,0);
	
	// Add the expiration controls
	QHBoxLayout *hbox = new QHBoxLayout();
	topGrid->addLayout(hbox,1,1);
	
	m_countdownLabel = new QLabel(QString("Expires in %1 Minutes").arg(m_countdownValue));
	connect(&m_expiresCountdownTimer, SIGNAL(timeout()), this, SLOT(countdownExpires()));
	m_expiresCountdownTimer.setInterval(1000 * 60);
	
	m_resetCounterBtn = new QPushButton("Reset Timer");
	connect(m_resetCounterBtn, SIGNAL(clicked()), this, SLOT(resetCountdown()));
	
	hbox->addWidget(m_countdownLabel);
	hbox->addWidget(m_resetCounterBtn);
	
	// Now add the control to revert control of the lights to the wall switch
	m_wallControlButton = new QRadioButton("Wall Switch Control");
	connect(m_wallControlButton, SIGNAL(toggled(bool)), SLOT(setWallSwitchEnabled(bool)));
	
	topGrid->addWidget(m_wallControlButton,0,0);
	
	// Label to indicate the switch status
	m_switchLabel = new QLabel("Switch: (Unknown)");
	topGrid->addWidget(m_switchLabel,0,1);
	
	// Finish off the top grid with a spacer item
	topGrid->addItem(new QSpacerItem(1,1,QSizePolicy::Expanding),0,2);
	
	
	// Now to build the controls for the bottom
	// Basically, it's a group box containing a series of preset (a "thick channel")
	// There are a few special elements:
	// - Master Fader (override the values of EVERYTHING in the system - really tied into the MasterOutputWindow, but shown here for usability)
	// - Crossfade Speed setting (maybe a horizontal slider)
	// - "New" preset button to add a preset
	// - Method of editing/deleting a preset - TODO in future?
	// - Buttons to crossfade to the preset
	// - Sliders to manually set level of preset
	/// TODO : Custom curve mapping per channel - example:
	// At phc, the right wing 25% "appears" brighter than the left wing at 25% - so apply level mapping so 25% appears the same. Future feature tho.
	
	GetSettingsObject();
	
	m_controlWidgetBase = new QGroupBox("Lighting Controls");
	m_controlWidgetBase->setObjectName("m_controlWidgetBase");
	vbox->addWidget(m_controlWidgetBase);
	
	QVBoxLayout *ctrlVBox = new QVBoxLayout();
	m_controlWidgetBase->setLayout(ctrlVBox);
	
	QHBoxLayout *ctrlHBox = new QHBoxLayout();
	
	QPushButton *btnOutputs = new QPushButton("Output Channels...");
	connect(btnOutputs, SIGNAL(clicked()), this, SLOT(showOutputWindow()));
	ctrlHBox->addWidget(btnOutputs);
	
	
	QPushButton *btnPreset = new QPushButton("Add Preset...");
	connect(btnPreset, SIGNAL(clicked()), this, SLOT(addNewPreset()));
	ctrlHBox->addWidget(btnPreset);
	
	ctrlHBox->addStretch(1);
	
	ctrlHBox->addWidget(new QLabel("Cross-Fade Speed: "));
	
	QSlider *fadeSlider = new QSlider(Qt::Horizontal, m_controlWidgetBase);
	fadeSlider->setMinimum(0);
	fadeSlider->setMaximum(9999);
	fadeSlider->setValue(settings.value("crossFadeSpeed",2000).toInt());
	ctrlHBox->addWidget(fadeSlider);
	
	m_fadeSpeedBox = new QSpinBox();
	m_fadeSpeedBox->setMinimum(0);
	m_fadeSpeedBox->setMaximum(9999);
	m_fadeSpeedBox->setValue(settings.value("crossFadeSpeed",2000).toInt());
	m_fadeSpeedBox->setSuffix(" ms");
	connect(m_fadeSpeedBox, SIGNAL(valueChanged(int)), this, SLOT(setFadeSpeed(int)));
	connect(m_fadeSpeedBox, SIGNAL(valueChanged(int)), fadeSlider, SLOT(setValue(int)));
	connect(fadeSlider, SIGNAL(valueChanged(int)), m_fadeSpeedBox, SLOT(setValue(int)));
	
	ctrlHBox->addWidget(m_fadeSpeedBox);
	
	#ifdef HAVE_MIDITCP
	QPushButton *btnMidi = new QPushButton("Midi Setup...");
	connect(btnMidi, SIGNAL(clicked()), this, SLOT(showMidiSettingsDialog()));
	ctrlHBox->addWidget(btnMidi);
	#endif
	
	ctrlVBox->addLayout(ctrlHBox);
	
	m_sliderGrid = new QGridLayout();
	ctrlVBox->addLayout(m_sliderGrid);
	
	// Generate initial slider UI - will regenerate after closing preset editor window or adding new preset 
	generateSliderUI();
}
	
void MainWindow::showMidiSettingsDialog()
{
	#ifdef HAVE_MIDITCP
	MidiInputSettingsDialog *d = new MidiInputSettingsDialog(SliderMidiInputAdapter::instance(),this);
	d->exec();
	#endif
}

void MainWindow::generateSliderUI()
{
	// Clear the slider grid of old controls
	while(m_sliderGrid->count() > 0)
	{
		QLayoutItem *item = m_sliderGrid->itemAt(m_sliderGrid->count() - 1);
		m_sliderGrid->removeItem(item);
		if(QWidget *widget = item->widget())
		{
			// disconnect any slots
			disconnect(widget, 0, this, 0);
			
			m_sliderGrid->removeWidget(widget);
			delete widget;
		}
			
		delete item;
		item = 0;
	}
	
	QGridLayout *lay = m_sliderGrid;
	
	// Rebuild UI
	QSlider *slider;
	QSpinBox *spin;
	QPushButton *btn;
	
	QList<QWidget*> wSliders;
	QList<QWidget*> wValues;
	QList<QWidget*> wButtons;
	
	int row =0;
	int col =0;
	lay->addWidget(new QLabel("<b>Mains</b>"), row++, col, Qt::AlignHCenter);
	
	slider = new QSlider(Qt::Vertical);
	slider->setMinimum(0);
	slider->setMaximum(255);
	slider->setValue(255);
	connect(slider, SIGNAL(valueChanged(int)), this, SLOT(setMainLevel(int)));
	wSliders << slider;
	m_mainSlider = slider;
	lay->addWidget(slider, row++, col, Qt::AlignHCenter);
	
	spin = new QSpinBox();
	spin->setMinimum(0);
	spin->setMaximum(255);
	spin->setValue(255);
	connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
	connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
	lay->addWidget(spin, row++, col);
	wValues << spin;
	
	//btn = new QPushButton("Black");
	m_blackButton = new QPushButton(QIcon("/usr/share/icons/Bluecurve/32x32/stock/stock-media-stop.png"),"&Black");
	m_blackButton->setCheckable(true);
	connect(m_blackButton, SIGNAL(toggled(bool)), this, SLOT(fadeBlack(bool)));
	lay->addWidget(m_blackButton, row++, col);
	wButtons << m_blackButton;
	
	col++;
	
	int counter = 0;
	
	// Sort m_channelList by channel num
	qSort(m_presets.begin(), m_presets.end(), SimpleChannel::comparitor);
	
	foreach(LightingPreset *preset, m_presets)
	{
		qDebug() << "Generating UI for preset "<<preset->name();
		row = 0;
		
		btn = new QPushButton(preset->name());
		// TODO : Set menu on button
		// TODO : Set icon on button when :live:
		
		/// NB pass btn as the menu parent so that when the layout is cleared (above), the menu is destroyed along with the button
		btn->setMenu(createPresetMenu(preset, btn));
		lay->addWidget(btn, row++, col);
		
		// Start a new widget hash
		WidgetHash widgets;
		//const QPointer<QWidget*> type = QPointer<QWidget*>(btn); 
		widgets["nameBtn"] = btn;
		
		slider = new QSlider(Qt::Vertical);
		slider->setMinimum(0);
		slider->setMaximum(255);
		slider->setValue(preset->value());
		slider->setProperty("num",counter);
		connect(slider, SIGNAL(valueChanged(int)), this, SLOT(presetLevelChanged(int)));
		wSliders << slider;
		lay->addWidget(slider, row++, col, Qt::AlignHCenter);
		
		spin = new QSpinBox();
		spin->setMinimum(0);
		spin->setMaximum(255);
		spin->setValue(preset->value());
		connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
		connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
		lay->addWidget(spin, row++, col);
		wValues << spin;
		
		btn = new QPushButton(QIcon("/usr/share/icons/Bluecurve/32x32/stock/stock-media-play.png"),"");
		btn->setProperty("num",counter);
		connect(btn, SIGNAL(clicked()), this, SLOT(presetPlayButtonClicked()));
		lay->addWidget(btn, row++, col);
		wButtons << btn;
		
		widgets["slider"] = slider;
		widgets["playBtn"] = btn; 
		
		// Store UI element pointers for updates later
		preset->setWidgets(widgets);
		
		col++;
		counter++;
	}
	
	lay->addItem(new QSpacerItem(1,1,QSizePolicy::Expanding),row,col++);
	
	QWidget *lw = 0;
	QList<QWidget*> allWidgets  = QList<QWidget*>() << wSliders << wValues << wButtons;
	foreach(QWidget *w, allWidgets)
	{
		if(lw)
			QWidget::setTabOrder(lw,w);
		lw = w;
	}
	
	row++;
	lay->addWidget(new QLabel("<font color=gray><i>Hint:</i> To \"cut in\" (not fade-in) a preset, hold down SHIFT while pressing the Play button</font>"), row, 0, 1, col-1);
}


QMenu *MainWindow::createPresetMenu(LightingPreset *preset, QWidget *parent)
{
	QMenu *menu = new QMenu(parent);
	
	QAction *action;
	
	action = new QAction("Edit ...", parent);
	action->setProperty("num",preset->num());
	action->setIcon(QIcon("/usr/share/icons/Bluecurve/16x16/actions/edit.png"));
	connect(action, SIGNAL(triggered()), this, SLOT(presetEdit()));
	menu->addAction(action);
	menu->setDefaultAction(action);
	
	menu->addSeparator();
	
	action = new QAction("Delete", parent);
	action->setProperty("num",preset->num());
	action->setIcon(QIcon("/usr/share/icons/Bluecurve/16x16/stock/stock-delete.png"));
	connect(action, SIGNAL(triggered()), this, SLOT(presetDelete()));
	menu->addAction(action);
	
	return menu;
	
}

void MainWindow::setFadeSpeed(int speed, bool temp)
{
	if(temp && !m_fadeSpeedIsTemp)
	{
		m_fadeSpeedIsTemp = true;
		m_oldFadeSpeed = m_fadeSpeedBox->value();
		qDebug() << "MainWindow::setFadeSpeed(): Speed "<<speed<<" is temp, old: "<<m_oldFadeSpeed;
	}
	
	if(m_fadeSpeedBox->value() != speed)
		m_fadeSpeedBox->setValue(speed);
	
	if(m_blackAnim)
		m_blackAnim->setDuration( m_fadeSpeedBox->value() );
}
	
void MainWindow::setMainLevel(int level)
{
	if(m_mainSlider->value() != level)
		m_mainSlider->setValue(level);
		
	m_mainLevel = level;
	
	// OutputWindow actually handles this
	emit mainLevelChanged(level);
	
	// Only un-check the "Black" button if the black animation isn't running
	// because if we change it in response a call from the QPropertyAnimation
	// for black, well, that would be kinda stupid.
	if(level > 0 &&
	   (!m_blackAnim || (m_blackAnim &&
	     m_blackAnim->state() == QAbstractAnimation::Stopped)) &&
	   m_blackButton->isChecked())
	{
		m_isBlack = false;
		m_blackButton->setChecked(false);
	}
}

void MainWindow::fadeBlack(bool flag)
{
	if(flag != m_isBlack)
	{
		if(!m_blackAnim)
		{
			m_blackAnim = new QPropertyAnimation(this, "mainLevel");
			setFadeSpeed(m_fadeSpeedBox->value());
		}
			
			
		if(m_blackAnim->state() != QAbstractAnimation::Stopped)
			m_blackAnim->stop();
			
		m_blackAnim->setEndValue( flag ? 0 : 255 );
		m_blackAnim->start();
	
		m_isBlack = flag;
	}
}

void MainWindow::playPreset(int num, int endValue)
{
	LightingPreset *preset = m_presets[num];
	playPreset(preset, endValue);
}
	
void MainWindow::playPreset(LightingPreset *preset, int endValue)
{
	if(endValue < 0)
		endValue = preset->value() > 0 ? 0 : 255;
	bool cutFlag = (QApplication::keyboardModifiers() & Qt::ShiftModifier);
	if(m_fadeSpeedBox->value() == 0 || cutFlag)
	{
		preset->setValue(endValue);
		return;
	}
	
	QPropertyAnimation *anim = preset->valueAnim();
	anim->setDuration( m_fadeSpeedBox->value() );
	anim->setProperty("num",preset->num());
	//anim->setEasingCurve(QEasingCurve::OutQuad);
	disconnect(anim, 0, this, 0);
	connect(anim, SIGNAL(finished()), this, SLOT(valueAnimFinished()));
		
	if(anim->state() != QAbstractAnimation::Stopped)
		anim->stop();
		
	qDebug() << "MainWindow::presetPlayButtonClicked(): Current value of preset:" <<preset->value();
	anim->setEndValue( endValue );
	anim->start();
	
	WidgetHash widgets = preset->widgets();
	widgets["playBtn"]->setEnabled(false);
	
}

void MainWindow::valueAnimFinished()
{
	QPropertyAnimation *anim = dynamic_cast<QPropertyAnimation*>(sender());
	if(!anim)
		return;
	
	int num = anim->property("num").toInt();
	LightingPreset *preset = m_presets[num];
	WidgetHash widgets = preset->widgets();
	widgets["playBtn"]->setEnabled(true);

	QSlider *slider = dynamic_cast<QSlider*>(widgets["slider"]);
	if(!slider)
		return;
	slider->setValue(preset->value());
	
	if(m_fadeSpeedIsTemp)
	{
		qDebug() << "MainWindow::valueAnimFinished(): Resetting fade speed:" <<m_oldFadeSpeed;
		m_fadeSpeedIsTemp = false;
		setFadeSpeed(m_oldFadeSpeed);
	}
}

void MainWindow::setPresetLevel(int num, int level)
{
	if(num < 0 || num >= m_presets.size())
		return;
		
	LightingPreset *preset = m_presets[num];
	preset->setValue(level);
}

void MainWindow::showOutputWindow()
{
	m_outputWin->raise();
	m_outputWin->show();
}

void MainWindow::addNewPreset()
{
	LightingPreset *preset = new LightingPreset("New Preset", 1);
	
	DmxChannelList chans = m_outputWin->channelList();
	PresetValueList values;
	foreach(DmxChannel *chan, chans)
		values << new PresetValue(chan, chan->value());
	
	preset->setValues(values);
	
	addPreset(preset);
	presetEdit(preset);
	
}

void MainWindow::addPreset(LightingPreset *preset)
{
	preset->setNum(m_presets.size());
	qDebug() << "MainWindow::addPreset: preset:"<<preset->name()<<", num:"<<preset->num(); 
	
	connect(preset, SIGNAL(valueChanged(int)), this, SLOT(presetValueChanged(int)));
	//connect(preset, SIGNAL(numChanged(int)), this, SLOT(presetNumChanged(int)));
	connect(preset, SIGNAL(nameChanged(QString)), this, SLOT(presetNameChanged(QString)));
	
	m_presets << preset;
	
	generateSliderUI();
	//presetEdit(preset);
}

void MainWindow::presetNumChanged(int num)
{
// 	LightingPreset *preset = dynamic_cast<LightingPreset*>(sender());
// 	if(!preset)
// 	{
// 		qDebug() << "MainWindow::presetNumChanged: Unable to dynamic_cast<LightingPreset*>(sender())";
// 		return;
// 	}

	PresetEditWindow *win = dynamic_cast<PresetEditWindow*>(sender());
	if(!win)
	{
		qDebug() << "MainWindow::presetNumChanged: Unable to dynamic_cast<PresetEditWindow*>(sender())";
		return;
	}
	
	LightingPreset *preset = win->preset();
	
	if(num<0)
		num = 0;
	if(num >= m_presets.size())
		num = m_presets.size()-1;
	
	if(preset->num() == num)
	{
		qDebug() << "MainWindow::presetNumChanged: nums match, not changing";
		return;	
	}
	
	qDebug() << "MainWindow::presetNumChanged: preset:"<<preset->name()<<", new num:"<<num;
		
	preset->setNum(num);
	
	m_presets.removeAll(preset);
	m_presets.insert(num, preset);
	
	for(int i=num+1;i<m_presets.size();i++)
	{
		qDebug() << "MainWindow::presetNumChanged: Renumbered "<<m_presets[i]->name()<<" to "<<i;
		m_presets[i]->setNum(i);
	}
	
	generateSliderUI();
}

void MainWindow::presetDelete()
{
	QObject *senderObject = sender(); 
	int num = senderObject ? senderObject->property("num").toInt() : -1; 
	if(num < 0) 
	{ 
		qDebug() << "Error reading slider number, nothing changed."; 
		return; 
	} 
	
	LightingPreset *preset = m_presets[num];
	m_presets.removeAll(preset);
	
	if(m_editWindows.contains(preset))
	{
		PresetEditWindow *win = m_editWindows.value(preset);
		disconnect(win, 0, this, 0);
		win->close();
		win->deleteLater();
	}
	
	disconnect(preset, 0, this, 0);
	preset->deleteLater();
	preset = 0;
	
	generateSliderUI();
	
}

void MainWindow::presetEdit(LightingPreset *preset)
{
	if(!preset)
	{
		QObject *senderObject = sender(); 
		int num = senderObject ? senderObject->property("num").toInt() : -1; 
		if(num < 0) 
		{ 
			qDebug() << "Error reading slider number, nothing changed."; 
			return; 
		} 
		preset = m_presets[num];
	}
	
	PresetEditWindow *win = 0;
	if(m_editWindows.contains(preset))
	{
		win = m_editWindows.value(preset);
	}
	else
	{
		win = new PresetEditWindow(preset);
		connect(win, SIGNAL(presetNumChanged(int)), this, SLOT(presetNumChanged(int)));
		connect(win, SIGNAL(editorClosed(LightingPreset*)), this, SLOT(storePresets()));
		m_editWindows[preset] = win;
	}
	
	win->show();
	win->raise();
	win->setFocus();
	
}

//void MainWindow::presetMenuButtonClicked();
void MainWindow::presetPlayButtonClicked()
{
	QObject *senderObject = sender();
	int num = senderObject ? senderObject->property("num").toInt() : -1;
	if(num < 0)
	{
		qDebug() << "Error reading slider number, nothing changed.";
		return;
	}
	
	playPreset(num, -1);
}

#define GetLightingPreset() \
	QObject *senderObject = sender(); \
	int num = senderObject ? senderObject->property("num").toInt() : -1; \
	if(num < 0) \
	{ \
		qDebug() << "Error reading slider number, nothing changed."; \
		return; \
	} \
	LightingPreset *preset = m_presets[num]; 
	
void MainWindow::presetLevelChanged(int val)
{
	GetLightingPreset();
	
	preset->setValue(val);
}

void MainWindow::presetNameChanged(QString name)
{
	LightingPreset *preset = dynamic_cast<LightingPreset*>(sender());
	if(!preset)
	{
		qDebug() << "Unable to find slider for preset";
		return;
	}
	
	QPushButton *nameBtn = dynamic_cast<QPushButton*>(preset->widgets().value("nameBtn"));
	nameBtn->setText(name);
}

void MainWindow::presetValueChanged(int val)
{
	//qDebug() << "MainWindow::presetValueChanged(): new value:"<<val;
	
	LightingPreset *preset = dynamic_cast<LightingPreset*>(sender());
	QPropertyAnimation *anim = preset->valueAnim();
	if(anim->state() != QAbstractAnimation::Stopped)
		return;
	
	//LightingPreset *preset = dynamic_cast<LightingPreset*>(sender());
	if(!preset)
	{
		qDebug() << "Unable to find slider for preset";
		return;
	}
	
	WidgetHash widgets = preset->widgets();
	QSlider *slider = dynamic_cast<QSlider*>(widgets["slider"]);
	if(!slider)
	{
		qDebug() << "Error finding slider for preset";
		return;
	}
	
	if(slider->value() != val)
		slider->setValue(val);
	
}
